#include "ADUGamePlayScene.h"
#include "ADUDuck.h"
#include "ADUFlow.h"
#include "GB2ShapeCache-x.h"
#include "ADUGarbage.h"
#include "Utils.h"
#include "SoundManager.h"
#include "ADUAppDelegate.h"
#include "ADUJavaCaller.h"
#include <CCPhysicsSprite.h>

USING_NS_CC;
USING_NS_CC_EXT;

FollowLayer*   GamePlayLayer::mFllowLayer;

Scene* GamePlayLayer::createScene()
{
    auto scene = Scene::create();
    auto layer = GamePlayLayer::create();
    scene->addChild(layer,1);
    mFllowLayer = FollowLayer::create(layer);
    scene->addChild(mFllowLayer,0);
    return scene;
}

bool GamePlayLayer::init()
{
	mt = 0;
	mAllowFlow = false;
	mPaused = false;
	isMusicPlaying = true;
    mHand = NULL;
    if ( !Layer::init() )
    {
        return false;
    }
    
    gameOver = false;
    playedTime = 0;

	// SoundManager::preloadMusic(MUSIC_BG);
	// SoundManager::playMusic(MUSIC_BG, true);
	GameManager::getInstance()->ResetWorld();
    mWorld = GameManager::getInstance()->mWorld;


    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    float centerX = origin.x + visibleSize.width/2;
    float centerY = origin.y + visibleSize.height/2;
    // Add Menu
	auto sample = Sprite::create(IMG_EXIT_PNG);
	
	float btn_width = sample->getContentSize().width;
	float btn_height = sample->getContentSize().height;
	
	// Add button
	mExitButton = Button::create(IMG_EXIT_PNG,IMG_EXIT_PNG);
    mExitButton->setPosition(Vec2(origin.x + btn_width, origin.y + visibleSize.height - btn_height/2));
    mExitButton->setPressedActionEnabled(true);
    mExitButton->addTouchEventListener(CC_CALLBACK_2(GamePlayLayer::touchExit, this));
    this->addChild(mExitButton, Z_MENU);

    mPauseButton = Button::create(IMG_PAUSE_PNG,IMG_PAUSE_PNG);
    mPauseButton->setPosition(Vec2(origin.x + visibleSize.width - btn_width, origin.y + visibleSize.height - btn_height/2));
    mPauseButton->setPressedActionEnabled(true);
    mPauseButton->addTouchEventListener(CC_CALLBACK_2(GamePlayLayer::touchPause, this));
    this->addChild(mPauseButton, Z_MENU);

    mSoundButton = Button::create(IMG_SOUND_PNG,IMG_SOUND_PNG);
    mSoundButton->setPosition(Vec2(origin.x + visibleSize.width - 2*btn_width,origin.y + visibleSize.height - btn_height/2));
    mSoundButton->setPressedActionEnabled(true);
    mSoundButton->addTouchEventListener(CC_CALLBACK_2(GamePlayLayer::touchSound, this));
    this->addChild(mSoundButton, Z_MENU);

    mMusicButton = Button::create(IMG_MUSIC_PNG,IMG_MUSIC_PNG);
    mMusicButton->setPosition(Vec2(origin.x + visibleSize.width - 3*btn_width,origin.y + visibleSize.height - btn_height/2));
    mMusicButton->setPressedActionEnabled(true);
    mMusicButton->addTouchEventListener(CC_CALLBACK_2(GamePlayLayer::touchMusic, this));
    this->addChild(mMusicButton, Z_MENU);

	mTopBackground = Sprite::create(IMG_TOP_BACKGROUND);
	Size mTopBgSize = mTopBackground->getContentSize();
	mTopBackground->setPosition(centerX, visibleSize.height - mTopBgSize.height/4);
	this->addChild(mTopBackground, Z_TOPBACKGROUND);

	// Create score label
	// Add game title
    mLabelScore = Text::create("0", FONT, FONT_SIZE * SCALE_FACTOR);
    mLabelScore->setPosition(Vec2(origin.x + btn_width + 200, origin.y + visibleSize.height - btn_height/2));
    mLabelScore->setColor(Color3B(255,255,255));
    this->addChild(mLabelScore,1000);

	// Add Pipe	
	mPipe = new Pipe();
	mPipe->createPipe(this);
	Vec2 pipeHeadPos = mPipe->getPipeHeadPosition();
	mFlow = new Flow(this, pipeHeadPos);
	mFlow->setPosition(origin);
	mPipe->SetFlow(mFlow);
	this->addChild(mFlow, Z_FLUID);
 
    // Add Water
    mWater = new Water(200,visibleSize.height/4);
    mWater->setMaxHeight(visibleSize.height * 0.75);
    this->addChild(mWater->GetDrawer(),Z_WATER);

    mPipe->setWater(mWater);

    createWall();

    JavaCaller::showAd();

	this->setTouchEnabled(true);
	this->scheduleUpdate();

    return true;
}


float GamePlayLayer::getTopBackgroundHeight(){
    return mTopBackground->getContentSize().height;
}


void GamePlayLayer::createWall() {
        auto s = Director::getInstance()->getVisibleSize();
        // Define ground body
        b2BodyDef groundBodyDef;
        groundBodyDef.position.Set(0,(-0.25)*getTopBackgroundHeight()/PTM_RATIO);        // bottom-left corner
        mGroundBody = mWorld->CreateBody(&groundBodyDef);

        // top
        {
                b2EdgeShape groundBox;
                b2FixtureDef groundFixtureDef;
                groundFixtureDef.shape = &groundBox;
                groundFixtureDef.restitution = 1.0f;
                groundFixtureDef.friction = 0.4f;

                // top
                groundBox.Set(b2Vec2(0/PTM_RATIO, s.height/PTM_RATIO),
                                  b2Vec2(s.width/PTM_RATIO, s.height/PTM_RATIO));
                mGroundBody->CreateFixture(&groundFixtureDef);
        }
}


void GamePlayLayer::ShowSpeedUpText(){
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    float centerX = origin.x + visibleSize.width/2;
    float centerY = origin.y + visibleSize.height/2;
    mSpeedUpText = Text::create("Speed Up", FONT, FONT_SIZE * SCALE_FACTOR);
    mSpeedUpText->setColor(Color3B::RED);
    
    mSpeedUpText->setPosition(Vec2(centerX, centerY));
    
    this->addChild(mSpeedUpText,1000);
    
    auto callback = CallFunc::create( this, callfunc_selector(GamePlayLayer::ShowSpeedUpTextDone) );
    
    auto sequence = Sequence::create(Blink::create(3, 3), callback, NULL);
    
    mSpeedUpText->runAction(sequence);
    
}
void GamePlayLayer::ShowSpeedUpTextDone(){
    this->removeChild(mSpeedUpText);
}


void GamePlayLayer::update(float dt) {
    GameManager::getInstance()->Step(dt);
	// Temp code for water splash - start

	if (mPaused) return;

	if (mt == 0)	{
		int i = rand()%200;
		mWater->Splash(-20.0f,i);
	}
	mt+=dt;
	if (mt > 3) {
        mt = 0;
        playedTime += 1;
        if (playedTime %5 == 0 && playedTime/5 != 0){
            GameManager::getInstance()->gameLevel = playedTime/5 + 1;
            ShowSpeedUpText();
        }
        
    }

    mFlow->update(dt,mAllowFlow);
    
	char buf[3];
	memset(buf,0,3);
	sprintf(buf,"Score: %d",GameManager::getInstance()->gameScore);
	mLabelScore->setText(buf);
	mWater->OnUpdate(dt);
    mFlow->setOffset(mFllowLayer->getPosition());
    GameManager::getInstance()->SetOffset(mFllowLayer->getPosition());
    mPipe->Update(dt);
    if (!gameOver && mFllowLayer->GetDuckHealth() == 0){
        gameOver = true;
        showGameOverPopUp();
    }

    mGroundBody->SetTransform((-1)*mFllowLayer->getPositionX()/PTM_RATIO, (-0.25)*getTopBackgroundHeight()/PTM_RATIO, 0);
}

void GamePlayLayer::OnExitClicked() {
	log("Exit");
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
        CCMessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
#else
        CCDirector::sharedDirector()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        exit(0);
#endif
#endif

}
void GamePlayLayer::OnPauseClicked() {
	log("Pause");
	mPaused = !mPaused;
	mPipe->SetGamePaused(mPaused);
}
void GamePlayLayer::OnSoundClicked() {
	log("Sound");
}
void GamePlayLayer::OnMusicClicked() {
	log("Music");
	if(isMusicPlaying) {
		SoundManager::pauseMusic();
	} else {
		SoundManager::resumeMusic();
	}
	isMusicPlaying = !isMusicPlaying;
}
bool GamePlayLayer::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *event) {
    log("Touch began");

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS || CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
		if (!mPipe->isPipeTouched(touch->getLocation())) {
			if(mAllowFlow) {
				mAllowFlow = false;
				mFlow->resetSpeed();
			} else {
				mAllowFlow = true;
			}
		}
#endif
}
void GamePlayLayer::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *event) {
    
}
void GamePlayLayer::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *event) {
	log("OnTouchEnd");
	/*if (mAllowFlow)
	{		
		//mAllowFlow = false;
		//mFlow->resetSpeed();
	}*/
}
void GamePlayLayer::onTouchCancelled(cocos2d::Touch *touch, cocos2d::Event *event) {
    log("OnTouchCancel");
	/*if (mAllowFlow)
	{
		mAllowFlow = false;
		mFlow->resetSpeed();
	}*/
    
}


void GamePlayLayer::setTouchEnabled(bool enabled)
{
    if (enabled)
    {
        mTouchListener = EventListenerTouchOneByOne::create();
        mTouchListener->retain();
        mTouchListener->onTouchBegan = CC_CALLBACK_2(GamePlayLayer::onTouchBegan, this);
		mTouchListener->onTouchMoved = CC_CALLBACK_2(GamePlayLayer::onTouchMoved, this);
		mTouchListener->onTouchEnded = CC_CALLBACK_2(GamePlayLayer::onTouchEnded, this);
		mTouchListener->onTouchCancelled = CC_CALLBACK_2(GamePlayLayer::onTouchCancelled, this);
        _eventDispatcher->addEventListenerWithSceneGraphPriority(mTouchListener, this);

    }
    else if (!enabled)
    {
        _eventDispatcher->removeEventListener(mTouchListener);
        mTouchListener->release();
        mTouchListener = nullptr;
    }

}

void GamePlayLayer::showGameOverPopUp() {
	this->setTouchEnabled(false);
	mPaused = true;
	mGameOverLayer = GameOverLayer::create();
	this->addChild(mGameOverLayer, 2000);
}


void GamePlayLayer::touchMusic(Ref *pSender, Widget::TouchEventType type) {
    switch (type)
    {
        case Widget::TouchEventType::BEGAN:
            break;

        case Widget::TouchEventType::ENDED:
        case Widget::TouchEventType::CANCELED:
        	OnMusicClicked();
            break;

        default:
            break;
    }
}

void GamePlayLayer::touchExit(Ref *pSender, Widget::TouchEventType type) {
    switch (type)
    {
        case Widget::TouchEventType::BEGAN:
            break;

        case Widget::TouchEventType::ENDED:
        case Widget::TouchEventType::CANCELED:
        	OnExitClicked();
            break;

        default:
            break;
    }
}

void GamePlayLayer::touchPause(Ref *pSender, Widget::TouchEventType type) {
    switch (type)
    {
        case Widget::TouchEventType::BEGAN:
            break;

        case Widget::TouchEventType::ENDED:
        case Widget::TouchEventType::CANCELED:
        	OnPauseClicked();
            break;

        default:
            break;
    }
}

void GamePlayLayer::touchSound(Ref *pSender, Widget::TouchEventType type) {
    switch (type)
    {
        case Widget::TouchEventType::BEGAN:
            break;

        case Widget::TouchEventType::ENDED:
        case Widget::TouchEventType::CANCELED:
        	OnSoundClicked();
            break;

        default:
            break;
    }
}

void GamePlayLayer::showTutorial() {

}


/////////////////////////////////////////////////////////////
//// FOLLOW LAYER ///////////////////////////////////////////

FollowLayer::~FollowLayer() {
	log("Tuyen1 - FollowLayer destructure");
    delete [] mBasin;
}

FollowLayer* FollowLayer::create(GamePlayLayer* layer) {
    FollowLayer *pRet = new FollowLayer(layer);

    if (pRet && pRet->init())
    {
        pRet->autorelease();
        return pRet;
    }
    else
    {
        delete pRet;
        pRet = NULL;
        return NULL;
    }
}


FollowLayer::FollowLayer(GamePlayLayer* layer){
    mPlayLayer = layer;
    mWater = layer->getWater();
}

bool FollowLayer::init()
{
//////////////////////////////
// 1. super init first

    if ( !Layer::init() )
    {
        return false;
    }
    mWorld = GameManager::getInstance()->mWorld;
    mBasin = new Basin*[NUM_BASIN];
    
#ifdef DEBUG_ON
    GLESDebugDraw* drawer = new GLESDebugDraw(PTM_RATIO);
    drawer->SetFlags(GLESDebugDraw::e_controllerBit);
    drawer->SetFlags(GLESDebugDraw::e_shapeBit);
    mWorld->SetDebugDraw(drawer);
    drawer->SetFlags(GLESDebugDraw::e_particleBit);
#endif


    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    float centerX = origin.x + visibleSize.width/2;
    float centerY = origin.y + visibleSize.height/2;

    mSprBatchNode = SpriteBatchNode::create(IMG_BACKGROUND);
    mSprBatchNode->setPosition(Vec2(0,0));
#ifndef DEBUG_ON
    this->addChild(mSprBatchNode, Z_BACKGROUND);
#endif
    for(int i=0; i< NUM_BACKGROUND; i++) {
        Sprite* sprBackground = Sprite::createWithTexture(mSprBatchNode->getTexture());
        sprBackground->setPosition(Vec2((i*2+1)*centerX, centerY));
        mSprBatchNode->addChild(sprBackground);
    }

    Size winSize = Director::getInstance()->getWinSize();
    Vec2 pos_basin(visibleSize.width/10, visibleSize.height/4);
    
    // Add "GO"
	auto go = Sprite::create(IMG_GO);
	Size goSize = go->getContentSize();
	go->setPosition(visibleSize.width/10 + goSize.width/2, centerY);
	this->addChild(go, Z_BACKGROUND);

    
    
    addBasinWithCoords(pos_basin,NUM_BASIN);
    addDuckWithCoords(ccp(pos_basin.x, visibleSize.height/3));
    
    
    mFish = Eatable::CreateEatable(IMG_FISH);
    mFish->setPosition(centerX, centerY);
    //mFish->AddPhysic();
    mFish->setTag(TAG_FISH);
    
    this->addChild(mFish, Z_FISH);
    
    //AddGarbage();

    follow(this,mSprDuck);    

    this->scheduleUpdate();
    return true;       
}


void FollowLayer::UpdateEatables(){
    if (!mFish) return;
    if(mSprDuck->getBoundingBox().containsPoint(mFish->getPosition())){
        Vec2 pos= mFish->getPosition();
        this->removeChild(mFish);
        
        CCParticleSun* explosion = CCParticleSun::create();
        explosion->setTexture(CCTextureCache::sharedTextureCache()->addImage(IMG_PARTICLE_FIRE));
        // explosion->initWithTotalParticles(200);
        explosion->setAutoRemoveOnFinish(true);
        explosion->setStartSize(10.0f);
        explosion->setSpeed(50.0f);
        explosion->setAnchorPoint(ccp(0.5f,0.5f));
        explosion->setPosition(pos);
        explosion->setDuration(1.0f);
        explosion->retain();
        
        addChild(explosion, 11);
        explosion->release();
        mFish = NULL;
    }
}


void FollowLayer::addDuckWithCoords(Vec2 p)
{
    mSprDuck = Duck::create(IMG_DUCK);
    mSprDuck->addHealthBar();
    mSprDuck->setPosition(p);
    mSprDuck->integrateBox2D();
    
    if(mWater) {
        mWater->AddListener(mSprDuck);
        mSprDuck->attachBody(mSprDuck->GetBody());
    } 
    this->addChild(mSprDuck, Z_DUCK);
}


void FollowLayer::AddGarbage() {
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    float posx = origin.x;
    float rear = origin.x+visibleSize.width;
    float d1   = visibleSize.height/20;
    float d2   = visibleSize.width/10;
    float waterHeight = mWater->getHeight();

    for (int i = 0; posx < rear; i++){
        int res = (int)rand() % 5;
        Garbage* garbage = Garbage::CreateGarbage(GARBAGE_RES_PATH[res]);
        Vec2 pos = Vec2(posx, waterHeight - d1 * res);
        
        b2BodyDef bodyDef;
        bodyDef.type = b2_dynamicBody;
        bodyDef.userData = garbage;
        bodyDef.position.Set(pos.x/PTM_RATIO, pos.y/PTM_RATIO);
        bodyDef.fixedRotation = false;
        b2Body *body = mWorld->CreateBody(&bodyDef);
        
        b2PolygonShape shape;
        float spriteWidth = garbage->getContentSize().width;
        float spriteHeight = garbage->getContentSize().height;
        shape.SetAsBox(0.5*spriteWidth/PTM_RATIO, 0.5*spriteHeight/PTM_RATIO);

        b2FixtureDef boxShapedef;
        boxShapedef.shape = &shape;
        boxShapedef.density =   DENSITY_GARBAGE;
        boxShapedef.restitution = 0.2f;
        boxShapedef.friction = 1.0f;

        b2Fixture* boxFixture;
        boxFixture = body->CreateFixture(&boxShapedef);


        garbage->setBody(body);
        garbage->setPosition(pos);
        this->addChild(garbage,Z_GARBAGE);
        mWater->AddBody(garbage->GetBody());
        posx += d2; 
    }
}
void FollowLayer::addBasinWithCoords(Vec2 p, int num)
{
    float delta = BASIN_DELTA;
    Vec2 prevPos;
    int ran;
    int waterheight = mPlayLayer->getWater()->getHeight();
    mBasin[0] = Basin::CreateBasin();
    p.y = waterheight - mBasin[0]->getContentSize().height/4;
    mBasin[0]->setPosition(p);
    mBasin[0]->integrateBox2D();
    mBasin[0]->setId(0);
    
    if(mWater) {
        mWater->AddBody(mBasin[0]->GetBody());
    }
    this->addChild(mBasin[0], Z_BASIN);
    
    for(int i=1; i < num; i++) {
        
        ran = rand()%BASIN_RANDOM_RANGE;
        
        prevPos = mBasin[i-1]->getPosition();
        mBasin[i] = Basin::CreateBasin();
        mBasin[i]->setPosition(prevPos.x+delta, prevPos.y);
        mBasin[i]->integrateBox2D();
        mBasin[i]->setId(i);
        
        if(mWater) {
            mWater->AddBody(mBasin[i]->GetBody());
        }
        this->addChild(mBasin[i], Z_BASIN);
    }
    Utils::basinHeight = mBasin[0]->getContentSize().height;
}

void FollowLayer::UpdateBasin(){
    float realPosX;
    float delta = BASIN_DELTA;
    int ran = rand()%BASIN_RANDOM_RANGE;
    realPosX = this->getPositionX() + mBasin[NUM_BASIN-1]->getPositionX();
    
    if (realPosX < 4*delta){
        
        Vec2 pos;
        mBasin[0]->Clean();
        this->removeChild(mBasin[0]);
        //delete mBasin[0]; don't need, it's freed by cocos2dx when there is no reference
        
        for (int i = 0; i < NUM_BASIN-1; i++){
            mBasin[i] = mBasin[i+1];
        }

        pos.y = mBasin[NUM_BASIN-1]->getPositionY();
        pos.x = mBasin[NUM_BASIN-1]->getPositionX()+delta + ran;
        mBasin[NUM_BASIN-1] = Basin::CreateBasin();
        mBasin[NUM_BASIN-1]->setPosition(pos);
        mBasin[NUM_BASIN-1]->integrateBox2D();
        mBasin[NUM_BASIN-1]->setId(mBasin[NUM_BASIN-2]->getId()+1);
        
        if(mWater) {
            mWater->AddBody(mBasin[NUM_BASIN-1]->GetBody());
        }
        this->addChild(mBasin[NUM_BASIN-1],Z_BASIN);
    }
}


void FollowLayer::UpdateScore(){
    float x;
    int score;
    Basin* basin;
    
    for (int i = 0; i < NUM_BASIN ; i++){
        basin = mBasin[i];
        x = basin->getPosition().x - basin->getContentSize().width/2;
        if (x > mSprDuck->getPosition().x){
            break;
        }
    }
    
    score = basin->getId() - 1;
    
    GameManager::getInstance()->gameScore = score;
}

void FollowLayer::update(float dt) {
    mSprDuck->update(dt);
    UpdateScore();
    UpdateBasin();
    //mGroundBody->SetTransform((-1)*this->getPositionX()/PTM_RATIO, (-0.25)*mPlayLayer->getTopBackgroundHeight()/PTM_RATIO, 0);
    UpdateEatables();
}

void FollowLayer::draw(Renderer *renderer, const Mat4 &transform,
    uint32_t flags) {
    CCNode::draw(renderer,transform,flags);
    kmGLPushMatrix();
#ifdef DEBUG_ON
    mWorld->DrawDebugData();
#endif
    kmGLPopMatrix();
}

void FollowLayer::follow(Node* follower, Node* target) {        
    Size visibleSize = Director::getInstance()->getWinSize();       
    auto followAction = Follow::create(target,Rect(0,0,visibleSize.width*NUM_BACKGROUND,visibleSize.height));       
    follower->runAction(followAction);      
}

int FollowLayer::GetDuckHealth(){
    return mSprDuck->getHealthStatus();
}




